Here we will map an example dataset from three leukemias sequenced in
our study. This tutorial will take approximately 10 minutes to run
Setup
library(Seurat)
library(tidyverse)
library(symphony)
library(ggpubr)
library(patchwork)
library(RColorBrewer)
Install package from github
## install dependencies that are not on CRAN
if(!require(BiocManager, quietly = TRUE)) install.packages("BiocManager")
BiocManager::install(c("AUCell", "doMC"))
if(!require(devtools, quietly = TRUE)) install.packages("devtools")
devtools::install_github("jaredhuling/jcolors")
## install BoneMarrowMap package
devtools::install_github('andygxzeng/BoneMarrowMap', force = TRUE)
library(BoneMarrowMap)
Download reference object and UMAP model
Set projection folder, Download reference object and UMAP model
# Set directory to store projection reference files
projection_path = './'
# Download Bone Marrow Reference - 344 Mb
curl::curl_download('https://bonemarrowmap.s3.us-east-2.amazonaws.com/BoneMarrowMap_SymphonyReference.rds',
destfile = paste0(projection_path, 'BoneMarrowMap_SymphonyReference.rds'))
# Download uwot model file - 221 Mb
curl::curl_download('https://bonemarrowmap.s3.us-east-2.amazonaws.com/BoneMarrowMap_uwot_model.uwot',
destfile = paste0(projection_path, 'BoneMarrowMap_uwot_model.uwot'))
projection_path = './'
# Load Symphony reference
ref <- readRDS(paste0(projection_path, 'BoneMarrowMap_SymphonyReference.rds'))
# Set uwot path for UMAP projection
ref$save_uwot_path <- paste0(projection_path, 'BoneMarrowMap_uwot_model.uwot')
Visualize Bone Marrow Reference
If we want to visualize celltype labels or metadata from the BM
Reference, we can create a Seurat Object from the symphony reference
This will be memory efficient as it will not include gene expression
counts, only the UMAP coordinates and the metadata including cell labels
and sorting information
ReferenceSeuratObj <- create_ReferenceObject(ref)
DimPlot(ReferenceSeuratObj, reduction = 'umap', group.by = 'CellType_Annotation_formatted',
raster=FALSE, label=TRUE, label.size = 4)

We can also visualize broader cell type labels which may simplify
interpretation and downstream analysis.
DimPlot(ReferenceSeuratObj, reduction = 'umap', group.by = 'CellType_Broad',
raster=FALSE, label=TRUE, label.size = 4)

We can visualize other annotations too, including cell cycle phase
and lineage pseudotime estimates.
p1 <- DimPlot(ReferenceSeuratObj, reduction = 'umap', group.by = 'CyclePhase', raster=FALSE)
p2 <- FeaturePlot(ReferenceSeuratObj, reduction = 'umap', features = 'Pseudotime', raster=FALSE)
p1 + p2

Load Leukemia scRNA-seq data.
We have confidently mapped leukemias spanning AML (incl. AMKL and
AEL), B-ALL, MPAL, MDS, CML, MPN, and BDPCN, across sequencing
technologies. However, we cannot be confident in mapping T-ALL due to a
lack of thymus-specific reference data on T cell precursor stages.
Further, our tool does not discriminate between normal vs malignant
cells, which is an important consideration particularly within low-blast
count chronic diseases.
As an example here, we are going to project scRNA-seq data from three
diverse AML patients sequenced in our study.
- pt_17844: MLL-AF9 translocation with a purely mature leukemia cell
hierarchy
- pt_17746: NPM1c + DNMT3A + TET2 with a GMP-dominant leukemia cell
hierarchy
- pt_30886: Complex Karyotype with a primitive leukemia cell hierarchy
+ extensive erythroid involvement
Each patient sample was downsampled to 2500 cells to decrease runtime
for this example.
curl::curl_download('https://bonemarrowmap.s3.us-east-2.amazonaws.com/ExampleQuery_AML_scRNAseq.rds',
destfile = paste0(projection_path, 'ExampleQuery_AML_scRNAseq.rds'))
query <- readRDS('ExampleQuery_AML_scRNAseq.rds')
query
Map the Query Data
Provide raw counts, metadata, and donor key. This should take <1
min Calculate mapping error and perform QC to remove low quality cells
with high mapping error
# batch variable to correct in the query data, set as NULL if no batches in query
batchvar <- 'Patient'
# Map query dataset using Symphony (Kang et al 2021)
query <- map_Query(
exp_query = query@assays$RNA@counts,
metadata_query = query@meta.data,
ref_obj = ref,
vars = batchvar
)
Normalizing
Scaling and synchronizing query gene expression
Found 2360 reference variable genes in query dataset
Project query cells using reference gene loadings
Clustering query cells to reference centroids
Correcting query batch effects
UMAP
All done!
Warning: Adding features not currently present in the object
In leukemia samples, the distribution of mapping error scores can
vary broadly from sample to sample. In this context, we will want to
threshold outliers with high mapping error on a per-sample basis.
Typically, a threshold of 2, 2.5, or 3 MADs works well.
In some cases where sequencing depth is very low (e.g. older datasets
from first-generation scRNA-seq protocols), a more stringent threshold
of even 1.5 may be warranted to eliminate cells with low mapping
quality
# Run QC based on mapping error score, flag cells with mapping error >= 2.5 MADs above median
query <- query %>% calculate_MappingError(., reference = ref, MAD_threshold = 2.5,
threshold_by_donor = TRUE, donor_key = batchvar) # threshold mapping error on a per-sample basis.
# Plot distribution by patient to ensure you are catching the tail
query@meta.data %>%
ggplot(aes(x = mapping_error_score, fill = mapping_error_QC)) +
geom_histogram(bins = 200) + facet_wrap(.~get(batchvar))

# Get QC Plots
QC_plots <- plot_MappingErrorQC(query)
# Plot together - If this is too crowded, can also just call "QC_plots" aloneto display one by one
patchwork::wrap_plots(QC_plots, ncol = 4, widths = c(0.8, 0.3, 0.8, 0.3))

This important step identifies a subset of cells with high mapping
error from the query dataset that are either:
- not present within the reference, or
- have poor QC metrics (low RNA counts and low transcriptional
diversity)
Sometimes, low quality cells may erroneously map to the
orthochromatic erythroblast region as this cell type has very low
transcriptional diversity. These low quality query cells do not have
hemoglobin expression and are in fact mis-mapped; they will be flagged
by the QC filter and excluded from cell type assignments.
Please adjust the MAD_threshold (typically between 1 and 3)
based on the distribution of your dataset to identify the outliers with
low quality and high mapping error scores. This will improve your
classifications and any downstream composition analysis
# # Optional step - remove outliers with high mapping error
# query <- subset(query, mapping_error_QC == 'Pass')
Optionally, outlier cells with high mapping error can also be removed
at this stage. For ease of integrating these mapped annotations with the
rest of your analysis, we can choose to skip this step. If so, Final
CellType and Pseudotime predictions will be assigned as NA for cells
failing the mapping error QC threshold.
Cell Type Assignments
We will next use a KNN classifier to assign cell identity based on
the 30 K-Nearest Neighbours from the reference map. Broader cell type
labels will also be transferred automatically along with the precise
cell type labels. This label transfer step will take longer, potentially
around 10 minutes for ~10,000 cells
# Predict Hematopoietic Cell Types by KNN classification
query <- predict_CellTypes(
query_obj = query,
ref_obj = ref,
initial_label = 'initial_CellType', # celltype assignments before filtering on mapping QC
final_label = 'predicted_CellType' # celltype assignments with map QC failing cells assigned as NA
)
DimPlot(subset(query, mapping_error_QC == 'Pass'), reduction = 'umap_projected', group.by = c('predicted_CellType'),
raster=FALSE, label=TRUE, label.size = 4)

We can also visualize the broader cell type categories in case
precise labels are too granular. These provide a simpler view of the
data and can help guide cluster annotations if your dataset does not
have many cells.
DimPlot(subset(query, mapping_error_QC == 'Pass'), reduction = 'umap_projected', group.by = c('predicted_CellType_Broad'),
raster=FALSE, label=TRUE, label.size = 4)

Pseudotime Annotations
We can also annotate each query cell based on their position along
hematopoietic pseudotime. Query cells will be assigned a pseudotime
score based on the 30 K-Nearest Neighbours from the reference map. Since
our Pseudotime KNN assignments are performed in UMAP space (more
accurate than KNN on harmony components), this step is very fast (<
10s)
# Predict Pseudotime values by KNN
query <- predict_Pseudotime(
query_obj = query,
ref_obj = ref,
initial_label = 'initial_Pseudotime', # pseudotime assignments before filtering on mapping QC
final_label = 'predicted_Pseudotime' # pseudotime assignments with map QC failing cells assigned as NA
)
# Visualize Hematopoietic Pseudotime in query data
FeaturePlot(subset(query, mapping_error_QC == 'Pass'), features = c('predicted_Pseudotime'), split.by = 'Patient') &
scale_color_gradientn(colors = rev(brewer.pal(11, 'RdBu')))
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

Visualize Projection Density
Now let’s visualize the density distribution of query cells across
the hematopoietic hierarchy
# Set batch/condition to be visualized individually
batch_key <- 'Patient'
# returns a list of plots for each donor from a pre-specified batch variable
projection_plots <- plot_Projection_byDonor(
query_obj = query,
batch_key = batch_key,
ref_obj = ref,
Hierarchy_only = FALSE, # Whether to exclude T/NK/Plasma/Stromal cells
downsample_reference = TRUE,
downsample_frac = 0.25, # down-sample reference cells to 25%; reduces figure file size
query_point_size = 0.2, # adjust size of query cells based on # of cells
saveplot = TRUE,
save_folder = 'projectionFigures/'
)
# show plots together with patchwork. Can also just call "projection_plots" object to display one-by-one
patchwork::wrap_plots(projection_plots, ncol = 3)

We can also set Hierarchy_only = TRUE to remove T/NK/Plasma/Stromal
cells and focus solely on the hematopoietic hierarchy.
# Set batch/condition to be visualized individually
batch_key <- 'Patient'
# returns a list of plots for each donor from a pre-specified batch variable
projection_plots <- plot_Projection_byDonor(
query_obj = query,
batch_key = batch_key,
ref_obj = ref,
Hierarchy_only = TRUE, # Whether to exclude T/NK/Plasma/Stromal cells
downsample_reference = TRUE,
downsample_frac = 0.25, # down-sample reference cells to 25%; reduces figure file size
query_point_size = 0.2, # adjust size of query cells based on # of cells
saveplot = TRUE,
save_folder = 'projectionFigures/'
)
# show plots together with patchwork. Can also just call "projection_plots" object to display one-by-one
patchwork::wrap_plots(projection_plots, ncol = 3)

Get Composition data for each donor
Here, to study the abundance of each cell type within each donor, I
focus on cells that were classified with a KNN prob > 0.5 (that is,
>50% of nearest neighbours from the reference map agree on the
assigned cell type).
We can present this as a long table
query_composition <- get_Composition(
query_obj = query,
donor_key = 'Patient',
celltype_label = 'predicted_CellType',
mapQC_col = 'mapping_error_QC',
knn_prob_cutoff = 0.5,
return_type = 'long')
query_composition
Or as a wide table with counts of # of cells
query_composition <- get_Composition(
query_obj = query,
donor_key = 'Patient',
celltype_label = 'predicted_CellType',
mapQC_col = 'mapping_error_QC',
knn_prob_cutoff = 0.5,
return_type = 'count')
query_composition
Or as a wide table with proportion of each cell type within each
donor
query_composition <- get_Composition(
query_obj = query,
donor_key = 'Patient',
celltype_label = 'predicted_CellType',
mapQC_col = 'mapping_error_QC',
knn_prob_cutoff = 0.5,
return_type = 'proportion')
query_composition
# Simple heatmap to visualize composition of projected samples
p <- query_composition %>%
# show celltypes present in >1% of total cells
select(Patient, colnames(query_composition)[-1][colSums(query_composition[-1]) > 0.01]) %>%
# convert to matrix and display heatmap
column_to_rownames('Patient') %>% data.matrix() %>% ComplexHeatmap::Heatmap()
p

Save projection results
This will save a csv file with the mapped annotations for each cell
(mapping error scores, umap coordinates, predicted Cell Type, and
predicted Pseudotime). We also have the option to save AUCell scores,
provided that they are in the metadata.
# Save CellType Annotations and Projected UMAP coordinates
save_ProjectionResults(
query_obj = query,
celltype_label = 'predicted_CellType',
celltype_KNNprob_label = 'predicted_CellType_prob',
pseudotime_label = 'predicted_Pseudotime',
save_AUCell_scores = TRUE,
file_name = 'querydata_projected_labeled.csv')
Note on Downstream Analysis:
For downstream analysis, you can use the projected celltype labels to
help annotate any leukemia cell clusters generated through unsupervised
dimensionality reduction and clustering from individual patients.
Sometimes, unsupervised analysis will yield clusters corresponding to
different developmental states and other times it may yield clusters
corresponding to distinct subclones within the patient. Along with tools
like inferCNV for patients with known cytogenetic abnormalities, this
can be integrated to visualize cellular hierarchies at the level of
individual subclones.
Additional information from pseudotime projection and scoring of
LSC-specific signatures can help identify candidate LSCs within the
data.
For downstream analysis, let’s take the example of pt30886, with
involvement of HSCs and multiple trajectories spanning Myeloid,
Erythroid, and pDC differentiation:
pt30886 <- query %>%
subset(., Patient == 'pt_30886') %>%
SCTransform() %>%
RunPCA() %>%
RunUMAP(reduction = 'pca', dims=1:20) %>%
FindNeighbors(reduction = 'pca', dims = 1:20) %>%
FindClusters(resolution = 1, algorithm = 3)
DimPlot(pt30886, reduction = 'umap', group.by=c('seurat_clusters', 'predicted_CellType_Broad'), label = T)

FeaturePlot(pt30886,
features = c('LSPC_Quiescent__Zeng2022_AUC', 'LSC_vs_Blast_UP__Ng2016_AUC', 'HSC_MPP_like__Top100_scAML_AUC'),
max.cutoff = 'q99', min.cutoff = 'q5', ncol = 3) &
scale_color_gradientn(colors = rev(brewer.pal(11, 'RdBu')))
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

Based on this we can see multiple transcriptional clusters within the
data, including some non-leukemic clusters of T cells, B cells, and
Plasma cells. Cluster 3 contains HSC/MPP-like cells most enriched for
LSC signatures.
Let’s see what each cluster looks like when projected onto
BoneMarrowMap:
DimPlot(pt30886, reduction = 'umap_projected', group.by=c('seurat_clusters', 'predicted_CellType_Broad'), label = T)

pt30886@meta.data %>%
# compare clusters against broad cell types assignments
group_by(predicted_CellType_Broad, seurat_clusters) %>%
summarise(count = n()) %>% ungroup() %>%
# get proportions of cells within each cluster assigned as a certain celltype
group_by(seurat_clusters) %>% mutate(prop = count / sum(count)) %>%
pivot_wider(id_cols = 'predicted_CellType_Broad', names_from = 'seurat_clusters', values_from = 'prop') %>%
# fill in zeros and format for heatmap
replace(is.na(.), 0) %>% column_to_rownames('predicted_CellType_Broad') %>%
# create a basic heatmap to visualize cell types vs clusters
pheatmap::pheatmap()
`summarise()` has grouped output by 'predicted_CellType_Broad'. You can override using the `.groups` argument.

This illustrates how annotations from BoneMarrowMap can allow us to
analyze clusters of leukemic cells identified from unsupervised
analysis. As shown in the heatmap above, each cluster from this patient
actually corresponds to a distinct state along the hematopoietic
hierarchy, with multiple clusters corresponding to erythroid
progenitors. Overlaying copy number alteration calls from inferCNV or
expressed mutations can help us identify the differentiation patterns of
distinct subclones within this patient.
Finally, for cohorts with many patients composition analysis can be
performed to understand how leukemia cell hierarchies vary with patient
characteristics and therapy response / relapse. I hope this tutorial was
useful and feel free to comment with any questions you may have around
projection of leukemia cells or downstream analysis.
LS0tCnRpdGxlOiAiTGV1a2VtaWEgUHJvamVjdGlvbiBUdXRvcmlhbCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKSGVyZSB3ZSB3aWxsIG1hcCBhbiBleGFtcGxlIGRhdGFzZXQgZnJvbSB0aHJlZSBsZXVrZW1pYXMgc2VxdWVuY2VkIGluIG91ciBzdHVkeS4KVGhpcyB0dXRvcmlhbCB3aWxsIHRha2UgYXBwcm94aW1hdGVseSAxMCBtaW51dGVzIHRvIHJ1biAKCiMjIyBTZXR1cAogCmBgYHtyfQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc3ltcGhvbnkpCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmBgYAoKSW5zdGFsbCBwYWNrYWdlIGZyb20gZ2l0aHViCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyMgaW5zdGFsbCBkZXBlbmRlbmNpZXMgdGhhdCBhcmUgbm90IG9uIENSQU4KaWYoIXJlcXVpcmUoQmlvY01hbmFnZXIsIHF1aWV0bHkgPSBUUlVFKSkgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQpCaW9jTWFuYWdlcjo6aW5zdGFsbChjKCJBVUNlbGwiLCAiZG9NQyIpKQppZighcmVxdWlyZShkZXZ0b29scywgcXVpZXRseSA9IFRSVUUpKSBpbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiamFyZWRodWxpbmcvamNvbG9ycyIpCmBgYAogCmBgYHtyLCBldmFsPUZBTFNFfQojIyBpbnN0YWxsIEJvbmVNYXJyb3dNYXAgcGFja2FnZQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoJ2FuZHlneHplbmcvQm9uZU1hcnJvd01hcCcsIGZvcmNlID0gVFJVRSkKYGBgCgpgYGB7cn0KbGlicmFyeShCb25lTWFycm93TWFwKQpgYGAKCiMjIyMgRG93bmxvYWQgcmVmZXJlbmNlIG9iamVjdCBhbmQgVU1BUCBtb2RlbAoKU2V0IHByb2plY3Rpb24gZm9sZGVyLCBEb3dubG9hZCByZWZlcmVuY2Ugb2JqZWN0IGFuZCBVTUFQIG1vZGVsCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBTZXQgZGlyZWN0b3J5IHRvIHN0b3JlIHByb2plY3Rpb24gcmVmZXJlbmNlIGZpbGVzCnByb2plY3Rpb25fcGF0aCA9ICcuLycKCiMgRG93bmxvYWQgQm9uZSBNYXJyb3cgUmVmZXJlbmNlIC0gMzQ0IE1iCmN1cmw6OmN1cmxfZG93bmxvYWQoJ2h0dHBzOi8vYm9uZW1hcnJvd21hcC5zMy51cy1lYXN0LTIuYW1hem9uYXdzLmNvbS9Cb25lTWFycm93TWFwX1N5bXBob255UmVmZXJlbmNlLnJkcycsIAogICAgICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gcGFzdGUwKHByb2plY3Rpb25fcGF0aCwgJ0JvbmVNYXJyb3dNYXBfU3ltcGhvbnlSZWZlcmVuY2UucmRzJykpCiMgRG93bmxvYWQgdXdvdCBtb2RlbCBmaWxlIC0gMjIxIE1iCmN1cmw6OmN1cmxfZG93bmxvYWQoJ2h0dHBzOi8vYm9uZW1hcnJvd21hcC5zMy51cy1lYXN0LTIuYW1hem9uYXdzLmNvbS9Cb25lTWFycm93TWFwX3V3b3RfbW9kZWwudXdvdCcsIAogICAgICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gcGFzdGUwKHByb2plY3Rpb25fcGF0aCwgJ0JvbmVNYXJyb3dNYXBfdXdvdF9tb2RlbC51d290JykpCmBgYAoKCmBgYHtyfQpwcm9qZWN0aW9uX3BhdGggPSAnLi8nCiMgTG9hZCBTeW1waG9ueSByZWZlcmVuY2UKcmVmIDwtIHJlYWRSRFMocGFzdGUwKHByb2plY3Rpb25fcGF0aCwgJ0JvbmVNYXJyb3dNYXBfU3ltcGhvbnlSZWZlcmVuY2UucmRzJykpCiMgU2V0IHV3b3QgcGF0aCBmb3IgVU1BUCBwcm9qZWN0aW9uCnJlZiRzYXZlX3V3b3RfcGF0aCA8LSBwYXN0ZTAocHJvamVjdGlvbl9wYXRoLCAnQm9uZU1hcnJvd01hcF91d290X21vZGVsLnV3b3QnKQpgYGAKCgojIyMjIFZpc3VhbGl6ZSBCb25lIE1hcnJvdyBSZWZlcmVuY2UKCklmIHdlIHdhbnQgdG8gdmlzdWFsaXplIGNlbGx0eXBlIGxhYmVscyBvciBtZXRhZGF0YSBmcm9tIHRoZSBCTSBSZWZlcmVuY2UsIHdlIGNhbiBjcmVhdGUgYSBTZXVyYXQgT2JqZWN0IGZyb20gdGhlIHN5bXBob255IHJlZmVyZW5jZSAKVGhpcyB3aWxsIGJlIG1lbW9yeSBlZmZpY2llbnQgYXMgaXQgd2lsbCBub3QgaW5jbHVkZSBnZW5lIGV4cHJlc3Npb24gY291bnRzLCBvbmx5IHRoZSBVTUFQIGNvb3JkaW5hdGVzIGFuZCB0aGUgbWV0YWRhdGEgaW5jbHVkaW5nIGNlbGwgbGFiZWxzIGFuZCBzb3J0aW5nIGluZm9ybWF0aW9uCgpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTF9ClJlZmVyZW5jZVNldXJhdE9iaiA8LSBjcmVhdGVfUmVmZXJlbmNlT2JqZWN0KHJlZikKCkRpbVBsb3QoUmVmZXJlbmNlU2V1cmF0T2JqLCByZWR1Y3Rpb24gPSAndW1hcCcsIGdyb3VwLmJ5ID0gJ0NlbGxUeXBlX0Fubm90YXRpb25fZm9ybWF0dGVkJywgCiAgICAgICAgcmFzdGVyPUZBTFNFLCBsYWJlbD1UUlVFLCBsYWJlbC5zaXplID0gNCkKYGBgCldlIGNhbiBhbHNvIHZpc3VhbGl6ZSBicm9hZGVyIGNlbGwgdHlwZSBsYWJlbHMgd2hpY2ggbWF5IHNpbXBsaWZ5IGludGVycHJldGF0aW9uIGFuZCBkb3duc3RyZWFtIGFuYWx5c2lzLiAKCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xMX0KRGltUGxvdChSZWZlcmVuY2VTZXVyYXRPYmosIHJlZHVjdGlvbiA9ICd1bWFwJywgZ3JvdXAuYnkgPSAnQ2VsbFR5cGVfQnJvYWQnLCAKICAgICAgICByYXN0ZXI9RkFMU0UsIGxhYmVsPVRSVUUsIGxhYmVsLnNpemUgPSA0KQpgYGAKCldlIGNhbiB2aXN1YWxpemUgb3RoZXIgYW5ub3RhdGlvbnMgdG9vLCBpbmNsdWRpbmcgY2VsbCBjeWNsZSBwaGFzZSBhbmQgbGluZWFnZSBwc2V1ZG90aW1lIGVzdGltYXRlcy4KCmBgYHtyLCBmaWcuaGVpZ2h0PTMuNSwgZmlnLndpZHRoPTExfQpwMSA8LSBEaW1QbG90KFJlZmVyZW5jZVNldXJhdE9iaiwgcmVkdWN0aW9uID0gJ3VtYXAnLCBncm91cC5ieSA9ICdDeWNsZVBoYXNlJywgcmFzdGVyPUZBTFNFKQpwMiA8LSBGZWF0dXJlUGxvdChSZWZlcmVuY2VTZXVyYXRPYmosIHJlZHVjdGlvbiA9ICd1bWFwJywgZmVhdHVyZXMgPSAnUHNldWRvdGltZScsIHJhc3Rlcj1GQUxTRSkgCgpwMSArIHAyCmBgYAoKCiMjIyMgTG9hZCBMZXVrZW1pYSBzY1JOQS1zZXEgZGF0YS4KCldlIGhhdmUgY29uZmlkZW50bHkgbWFwcGVkIGxldWtlbWlhcyBzcGFubmluZyBBTUwgKGluY2wuIEFNS0wgYW5kIEFFTCksIEItQUxMLCBNUEFMLCBNRFMsIENNTCwgTVBOLCBhbmQgQkRQQ04sIGFjcm9zcyBzZXF1ZW5jaW5nIHRlY2hub2xvZ2llcy4KSG93ZXZlciwgd2UgY2Fubm90IGJlIGNvbmZpZGVudCBpbiBtYXBwaW5nIFQtQUxMIGR1ZSB0byBhIGxhY2sgb2YgdGh5bXVzLXNwZWNpZmljIHJlZmVyZW5jZSBkYXRhIG9uIFQgY2VsbCBwcmVjdXJzb3Igc3RhZ2VzLiBGdXJ0aGVyLCBvdXIgdG9vbCBkb2VzIG5vdCBkaXNjcmltaW5hdGUgYmV0d2VlbiBub3JtYWwgdnMgbWFsaWduYW50IGNlbGxzLCB3aGljaCBpcyBhbiBpbXBvcnRhbnQgY29uc2lkZXJhdGlvbiBwYXJ0aWN1bGFybHkgd2l0aGluIGxvdy1ibGFzdCBjb3VudCBjaHJvbmljIGRpc2Vhc2VzLiAKCgpBcyBhbiBleGFtcGxlIGhlcmUsIHdlIGFyZSBnb2luZyB0byBwcm9qZWN0IHNjUk5BLXNlcSBkYXRhIGZyb20gdGhyZWUgZGl2ZXJzZSBBTUwgcGF0aWVudHMgc2VxdWVuY2VkIGluIG91ciBzdHVkeS4gCgoqIHB0XzE3ODQ0OiBNTEwtQUY5IHRyYW5zbG9jYXRpb24gd2l0aCBhIHB1cmVseSBtYXR1cmUgbGV1a2VtaWEgY2VsbCBoaWVyYXJjaHkKKiBwdF8xNzc0NjogTlBNMWMgKyBETk1UM0EgKyBURVQyIHdpdGggYSBHTVAtZG9taW5hbnQgbGV1a2VtaWEgY2VsbCBoaWVyYXJjaHkKKiBwdF8zMDg4NjogQ29tcGxleCBLYXJ5b3R5cGUgd2l0aCBhIHByaW1pdGl2ZSBsZXVrZW1pYSBjZWxsIGhpZXJhcmNoeSArIGV4dGVuc2l2ZSBlcnl0aHJvaWQgaW52b2x2ZW1lbnQgCgpFYWNoIHBhdGllbnQgc2FtcGxlIHdhcyBkb3duc2FtcGxlZCB0byAyNTAwIGNlbGxzIHRvIGRlY3JlYXNlIHJ1bnRpbWUgZm9yIHRoaXMgZXhhbXBsZS4gCgpgYGB7ciwgZXZhbD1GQUxTRX0KY3VybDo6Y3VybF9kb3dubG9hZCgnaHR0cHM6Ly9ib25lbWFycm93bWFwLnMzLnVzLWVhc3QtMi5hbWF6b25hd3MuY29tL0V4YW1wbGVRdWVyeV9BTUxfc2NSTkFzZXEucmRzJywgCiAgICAgICAgICAgICAgICAgICAgZGVzdGZpbGUgPSBwYXN0ZTAocHJvamVjdGlvbl9wYXRoLCAnRXhhbXBsZVF1ZXJ5X0FNTF9zY1JOQXNlcS5yZHMnKSkKYGBgCgoKYGBge3J9CnF1ZXJ5IDwtIHJlYWRSRFMoJ0V4YW1wbGVRdWVyeV9BTUxfc2NSTkFzZXEucmRzJykKcXVlcnkKYGBgCgoKIyMjIE1hcCB0aGUgUXVlcnkgRGF0YQpQcm92aWRlIHJhdyBjb3VudHMsIG1ldGFkYXRhLCBhbmQgZG9ub3Iga2V5LiBUaGlzIHNob3VsZCB0YWtlIDwxIG1pbgpDYWxjdWxhdGUgbWFwcGluZyBlcnJvciBhbmQgcGVyZm9ybSBRQyB0byByZW1vdmUgbG93IHF1YWxpdHkgY2VsbHMgd2l0aCBoaWdoIG1hcHBpbmcgZXJyb3IKCmBgYHtyfQojIGJhdGNoIHZhcmlhYmxlIHRvIGNvcnJlY3QgaW4gdGhlIHF1ZXJ5IGRhdGEsIHNldCBhcyBOVUxMIGlmIG5vIGJhdGNoZXMgaW4gcXVlcnkKYmF0Y2h2YXIgPC0gJ1BhdGllbnQnCgojIE1hcCBxdWVyeSBkYXRhc2V0IHVzaW5nIFN5bXBob255IChLYW5nIGV0IGFsIDIwMjEpCnF1ZXJ5IDwtIG1hcF9RdWVyeSgKICAgIGV4cF9xdWVyeSA9IHF1ZXJ5QGFzc2F5cyRSTkFAY291bnRzLCAKICAgIG1ldGFkYXRhX3F1ZXJ5ID0gcXVlcnlAbWV0YS5kYXRhLAogICAgcmVmX29iaiA9IHJlZiwKICAgIHZhcnMgPSBiYXRjaHZhcgopCmBgYAoKSW4gbGV1a2VtaWEgc2FtcGxlcywgdGhlIGRpc3RyaWJ1dGlvbiBvZiBtYXBwaW5nIGVycm9yIHNjb3JlcyBjYW4gdmFyeSBicm9hZGx5IGZyb20gc2FtcGxlIHRvIHNhbXBsZS4gSW4gdGhpcyBjb250ZXh0LCB3ZSB3aWxsIHdhbnQgdG8gdGhyZXNob2xkIG91dGxpZXJzIHdpdGggaGlnaCBtYXBwaW5nIGVycm9yIG9uIGEgcGVyLXNhbXBsZSBiYXNpcy4gVHlwaWNhbGx5LCBhIHRocmVzaG9sZCBvZiAyLCAyLjUsIG9yIDMgTUFEcyB3b3JrcyB3ZWxsLiAKCkluIHNvbWUgY2FzZXMgd2hlcmUgc2VxdWVuY2luZyBkZXB0aCBpcyB2ZXJ5IGxvdyAoZS5nLiBvbGRlciBkYXRhc2V0cyBmcm9tIGZpcnN0LWdlbmVyYXRpb24gc2NSTkEtc2VxIHByb3RvY29scyksIGEgbW9yZSBzdHJpbmdlbnQgdGhyZXNob2xkIG9mIGV2ZW4gMS41IG1heSBiZSB3YXJyYW50ZWQgdG8gZWxpbWluYXRlIGNlbGxzIHdpdGggbG93IG1hcHBpbmcgcXVhbGl0eSAKCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0xMH0KIyBSdW4gUUMgYmFzZWQgb24gbWFwcGluZyBlcnJvciBzY29yZSwgZmxhZyBjZWxscyB3aXRoIG1hcHBpbmcgZXJyb3IgPj0gMi41IE1BRHMgYWJvdmUgbWVkaWFuCnF1ZXJ5IDwtIHF1ZXJ5ICU+JSBjYWxjdWxhdGVfTWFwcGluZ0Vycm9yKC4sIHJlZmVyZW5jZSA9IHJlZiwgTUFEX3RocmVzaG9sZCA9IDIuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRocmVzaG9sZF9ieV9kb25vciA9IFRSVUUsIGRvbm9yX2tleSA9IGJhdGNodmFyKSAjIHRocmVzaG9sZCBtYXBwaW5nIGVycm9yIG9uIGEgcGVyLXNhbXBsZSBiYXNpcy4KCiMgUGxvdCBkaXN0cmlidXRpb24gYnkgcGF0aWVudCB0byBlbnN1cmUgeW91IGFyZSBjYXRjaGluZyB0aGUgdGFpbApxdWVyeUBtZXRhLmRhdGEgJT4lIAogIGdncGxvdChhZXMoeCA9IG1hcHBpbmdfZXJyb3Jfc2NvcmUsIGZpbGwgPSBtYXBwaW5nX2Vycm9yX1FDKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjAwKSArIGZhY2V0X3dyYXAoLn5nZXQoYmF0Y2h2YXIpKQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0xMH0KIyBHZXQgUUMgUGxvdHMKUUNfcGxvdHMgPC0gcGxvdF9NYXBwaW5nRXJyb3JRQyhxdWVyeSkKCiMgUGxvdCB0b2dldGhlciAtIElmIHRoaXMgaXMgdG9vIGNyb3dkZWQsIGNhbiBhbHNvIGp1c3QgY2FsbCAiUUNfcGxvdHMiIGFsb25ldG8gZGlzcGxheSBvbmUgYnkgb25lCnBhdGNod29yazo6d3JhcF9wbG90cyhRQ19wbG90cywgbmNvbCA9IDQsIHdpZHRocyA9IGMoMC44LCAwLjMsIDAuOCwgMC4zKSkKYGBgCgoKVGhpcyBpbXBvcnRhbnQgc3RlcCBpZGVudGlmaWVzIGEgc3Vic2V0IG9mIGNlbGxzIHdpdGggaGlnaCBtYXBwaW5nIGVycm9yIGZyb20gdGhlIHF1ZXJ5IGRhdGFzZXQgdGhhdCBhcmUgZWl0aGVyOgoKKiBub3QgcHJlc2VudCB3aXRoaW4gdGhlIHJlZmVyZW5jZSwgb3IKKiBoYXZlIHBvb3IgUUMgbWV0cmljcyAobG93IFJOQSBjb3VudHMgYW5kIGxvdyB0cmFuc2NyaXB0aW9uYWwgZGl2ZXJzaXR5KQoKU29tZXRpbWVzLCBsb3cgcXVhbGl0eSBjZWxscyBtYXkgZXJyb25lb3VzbHkgbWFwIHRvIHRoZSBvcnRob2Nocm9tYXRpYyBlcnl0aHJvYmxhc3QgcmVnaW9uIGFzIHRoaXMgY2VsbCB0eXBlIGhhcyB2ZXJ5IGxvdyB0cmFuc2NyaXB0aW9uYWwgZGl2ZXJzaXR5LiAKVGhlc2UgbG93IHF1YWxpdHkgcXVlcnkgY2VsbHMgZG8gbm90IGhhdmUgaGVtb2dsb2JpbiBleHByZXNzaW9uIGFuZCBhcmUgaW4gZmFjdCBtaXMtbWFwcGVkOyB0aGV5IHdpbGwgYmUgZmxhZ2dlZCBieSB0aGUgUUMgZmlsdGVyIGFuZCBleGNsdWRlZCBmcm9tIGNlbGwgdHlwZSBhc3NpZ25tZW50cy4KCioqUGxlYXNlIGFkanVzdCB0aGUgTUFEX3RocmVzaG9sZCAodHlwaWNhbGx5IGJldHdlZW4gMSBhbmQgMykgYmFzZWQgb24gdGhlIGRpc3RyaWJ1dGlvbiBvZiB5b3VyIGRhdGFzZXQgdG8gaWRlbnRpZnkgdGhlIG91dGxpZXJzIHdpdGggbG93IHF1YWxpdHkgYW5kIGhpZ2ggbWFwcGluZyBlcnJvciBzY29yZXMuIFRoaXMgd2lsbCBpbXByb3ZlIHlvdXIgY2xhc3NpZmljYXRpb25zIGFuZCBhbnkgZG93bnN0cmVhbSBjb21wb3NpdGlvbiBhbmFseXNpcyoqCgoKYGBge3J9CiMgIyBPcHRpb25hbCBzdGVwIC0gcmVtb3ZlIG91dGxpZXJzIHdpdGggaGlnaCBtYXBwaW5nIGVycm9yCiMgcXVlcnkgPC0gc3Vic2V0KHF1ZXJ5LCBtYXBwaW5nX2Vycm9yX1FDID09ICdQYXNzJykKYGBgCgpPcHRpb25hbGx5LCBvdXRsaWVyIGNlbGxzIHdpdGggaGlnaCBtYXBwaW5nIGVycm9yIGNhbiBhbHNvIGJlIHJlbW92ZWQgYXQgdGhpcyBzdGFnZS4KRm9yIGVhc2Ugb2YgaW50ZWdyYXRpbmcgdGhlc2UgbWFwcGVkIGFubm90YXRpb25zIHdpdGggdGhlIHJlc3Qgb2YgeW91ciBhbmFseXNpcywgd2UgY2FuIGNob29zZSB0byBza2lwIHRoaXMgc3RlcC4gSWYgc28sIEZpbmFsIENlbGxUeXBlIGFuZCBQc2V1ZG90aW1lIHByZWRpY3Rpb25zIHdpbGwgYmUgYXNzaWduZWQgYXMgTkEgZm9yIGNlbGxzIGZhaWxpbmcgdGhlIG1hcHBpbmcgZXJyb3IgUUMgdGhyZXNob2xkLiAKCgojIyMgQ2VsbCBUeXBlIEFzc2lnbm1lbnRzCldlIHdpbGwgbmV4dCB1c2UgYSBLTk4gY2xhc3NpZmllciB0byBhc3NpZ24gY2VsbCBpZGVudGl0eSBiYXNlZCBvbiB0aGUgMzAgSy1OZWFyZXN0IE5laWdoYm91cnMgZnJvbSB0aGUgcmVmZXJlbmNlIG1hcC4KQnJvYWRlciBjZWxsIHR5cGUgbGFiZWxzIHdpbGwgYWxzbyBiZSB0cmFuc2ZlcnJlZCBhdXRvbWF0aWNhbGx5IGFsb25nIHdpdGggdGhlIHByZWNpc2UgY2VsbCB0eXBlIGxhYmVscy4gClRoaXMgbGFiZWwgdHJhbnNmZXIgc3RlcCB3aWxsIHRha2UgbG9uZ2VyLCBwb3RlbnRpYWxseSBhcm91bmQgMTAgbWludXRlcyBmb3IgfjEwLDAwMCBjZWxscyAKCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xMH0KIyBQcmVkaWN0IEhlbWF0b3BvaWV0aWMgQ2VsbCBUeXBlcyBieSBLTk4gY2xhc3NpZmljYXRpb24KcXVlcnkgPC0gcHJlZGljdF9DZWxsVHlwZXMoCiAgcXVlcnlfb2JqID0gcXVlcnksIAogIHJlZl9vYmogPSByZWYsIAogIGluaXRpYWxfbGFiZWwgPSAnaW5pdGlhbF9DZWxsVHlwZScsICMgY2VsbHR5cGUgYXNzaWdubWVudHMgYmVmb3JlIGZpbHRlcmluZyBvbiBtYXBwaW5nIFFDCiAgZmluYWxfbGFiZWwgPSAncHJlZGljdGVkX0NlbGxUeXBlJyAgIyBjZWxsdHlwZSBhc3NpZ25tZW50cyB3aXRoIG1hcCBRQyBmYWlsaW5nIGNlbGxzIGFzc2lnbmVkIGFzIE5BCikgCgpEaW1QbG90KHN1YnNldChxdWVyeSwgbWFwcGluZ19lcnJvcl9RQyA9PSAnUGFzcycpLCByZWR1Y3Rpb24gPSAndW1hcF9wcm9qZWN0ZWQnLCBncm91cC5ieSA9IGMoJ3ByZWRpY3RlZF9DZWxsVHlwZScpLCAKICAgICAgICByYXN0ZXI9RkFMU0UsIGxhYmVsPVRSVUUsIGxhYmVsLnNpemUgPSA0KQpgYGAKCldlIGNhbiBhbHNvIHZpc3VhbGl6ZSB0aGUgYnJvYWRlciBjZWxsIHR5cGUgY2F0ZWdvcmllcyBpbiBjYXNlIHByZWNpc2UgbGFiZWxzIGFyZSB0b28gZ3JhbnVsYXIuIApUaGVzZSBwcm92aWRlIGEgc2ltcGxlciB2aWV3IG9mIHRoZSBkYXRhIGFuZCBjYW4gaGVscCBndWlkZSBjbHVzdGVyIGFubm90YXRpb25zIGlmIHlvdXIgZGF0YXNldCBkb2VzIG5vdCBoYXZlIG1hbnkgY2VsbHMuIAoKYGBge3IsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTEwfQpEaW1QbG90KHN1YnNldChxdWVyeSwgbWFwcGluZ19lcnJvcl9RQyA9PSAnUGFzcycpLCByZWR1Y3Rpb24gPSAndW1hcF9wcm9qZWN0ZWQnLCBncm91cC5ieSA9IGMoJ3ByZWRpY3RlZF9DZWxsVHlwZV9Ccm9hZCcpLCAKICAgICAgICByYXN0ZXI9RkFMU0UsIGxhYmVsPVRSVUUsIGxhYmVsLnNpemUgPSA0KQpgYGAKCgojIyMjIFBzZXVkb3RpbWUgQW5ub3RhdGlvbnMKV2UgY2FuIGFsc28gYW5ub3RhdGUgZWFjaCBxdWVyeSBjZWxsIGJhc2VkIG9uIHRoZWlyIHBvc2l0aW9uIGFsb25nIGhlbWF0b3BvaWV0aWMgcHNldWRvdGltZS4gClF1ZXJ5IGNlbGxzIHdpbGwgYmUgYXNzaWduZWQgYSBwc2V1ZG90aW1lIHNjb3JlIGJhc2VkIG9uIHRoZSAzMCBLLU5lYXJlc3QgTmVpZ2hib3VycyBmcm9tIHRoZSByZWZlcmVuY2UgbWFwLgpTaW5jZSBvdXIgUHNldWRvdGltZSBLTk4gYXNzaWdubWVudHMgYXJlIHBlcmZvcm1lZCBpbiBVTUFQIHNwYWNlIChtb3JlIGFjY3VyYXRlIHRoYW4gS05OIG9uIGhhcm1vbnkgY29tcG9uZW50cyksIHRoaXMgc3RlcCBpcyB2ZXJ5IGZhc3QgKDwgMTBzKQoKYGBge3IsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTEyfQojIFByZWRpY3QgUHNldWRvdGltZSB2YWx1ZXMgYnkgS05OCnF1ZXJ5IDwtIHByZWRpY3RfUHNldWRvdGltZSgKICBxdWVyeV9vYmogPSBxdWVyeSwgCiAgcmVmX29iaiA9IHJlZiwgCiAgaW5pdGlhbF9sYWJlbCA9ICdpbml0aWFsX1BzZXVkb3RpbWUnLCAgIyBwc2V1ZG90aW1lIGFzc2lnbm1lbnRzIGJlZm9yZSBmaWx0ZXJpbmcgb24gbWFwcGluZyBRQwogIGZpbmFsX2xhYmVsID0gJ3ByZWRpY3RlZF9Qc2V1ZG90aW1lJyAgICMgcHNldWRvdGltZSBhc3NpZ25tZW50cyB3aXRoIG1hcCBRQyBmYWlsaW5nIGNlbGxzIGFzc2lnbmVkIGFzIE5BCikKCiMgVmlzdWFsaXplIEhlbWF0b3BvaWV0aWMgUHNldWRvdGltZSBpbiBxdWVyeSBkYXRhCkZlYXR1cmVQbG90KHN1YnNldChxdWVyeSwgbWFwcGluZ19lcnJvcl9RQyA9PSAnUGFzcycpLCBmZWF0dXJlcyA9IGMoJ3ByZWRpY3RlZF9Qc2V1ZG90aW1lJyksIHNwbGl0LmJ5ID0gJ1BhdGllbnQnKSAmIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSByZXYoYnJld2VyLnBhbCgxMSwgJ1JkQnUnKSkpCmBgYAoKCiMjIyBWaXN1YWxpemUgUHJvamVjdGlvbiBEZW5zaXR5CgpOb3cgbGV0J3MgdmlzdWFsaXplIHRoZSBkZW5zaXR5IGRpc3RyaWJ1dGlvbiBvZiBxdWVyeSBjZWxscyBhY3Jvc3MgdGhlIGhlbWF0b3BvaWV0aWMgaGllcmFyY2h5CgpgYGB7ciwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9MTJ9CiMgU2V0IGJhdGNoL2NvbmRpdGlvbiB0byBiZSB2aXN1YWxpemVkIGluZGl2aWR1YWxseQpiYXRjaF9rZXkgPC0gJ1BhdGllbnQnCgojIHJldHVybnMgYSBsaXN0IG9mIHBsb3RzIGZvciBlYWNoIGRvbm9yIGZyb20gYSBwcmUtc3BlY2lmaWVkIGJhdGNoIHZhcmlhYmxlCnByb2plY3Rpb25fcGxvdHMgPC0gcGxvdF9Qcm9qZWN0aW9uX2J5RG9ub3IoCiAgcXVlcnlfb2JqID0gcXVlcnksIAogIGJhdGNoX2tleSA9IGJhdGNoX2tleSwgCiAgcmVmX29iaiA9IHJlZiwgCiAgSGllcmFyY2h5X29ubHkgPSBGQUxTRSwgIyBXaGV0aGVyIHRvIGV4Y2x1ZGUgVC9OSy9QbGFzbWEvU3Ryb21hbCBjZWxscyAKICBkb3duc2FtcGxlX3JlZmVyZW5jZSA9IFRSVUUsIAogIGRvd25zYW1wbGVfZnJhYyA9IDAuMjUsICAgIyBkb3duLXNhbXBsZSByZWZlcmVuY2UgY2VsbHMgdG8gMjUlOyByZWR1Y2VzIGZpZ3VyZSBmaWxlIHNpemUKICBxdWVyeV9wb2ludF9zaXplID0gMC4yLCAgICMgYWRqdXN0IHNpemUgb2YgcXVlcnkgY2VsbHMgYmFzZWQgb24gIyBvZiBjZWxscwogIHNhdmVwbG90ID0gVFJVRSwgCiAgc2F2ZV9mb2xkZXIgPSAncHJvamVjdGlvbkZpZ3VyZXMvJwopCgojIHNob3cgcGxvdHMgdG9nZXRoZXIgd2l0aCBwYXRjaHdvcmsuIENhbiBhbHNvIGp1c3QgY2FsbCAicHJvamVjdGlvbl9wbG90cyIgb2JqZWN0IHRvIGRpc3BsYXkgb25lLWJ5LW9uZQpwYXRjaHdvcms6OndyYXBfcGxvdHMocHJvamVjdGlvbl9wbG90cywgbmNvbCA9IDMpCmBgYAoKV2UgY2FuIGFsc28gc2V0IEhpZXJhcmNoeV9vbmx5ID0gVFJVRSB0byByZW1vdmUgVC9OSy9QbGFzbWEvU3Ryb21hbCBjZWxscyBhbmQgZm9jdXMgc29sZWx5IG9uIHRoZSBoZW1hdG9wb2lldGljIGhpZXJhcmNoeS4KCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0xMH0KIyBTZXQgYmF0Y2gvY29uZGl0aW9uIHRvIGJlIHZpc3VhbGl6ZWQgaW5kaXZpZHVhbGx5CmJhdGNoX2tleSA8LSAnUGF0aWVudCcKCiMgcmV0dXJucyBhIGxpc3Qgb2YgcGxvdHMgZm9yIGVhY2ggZG9ub3IgZnJvbSBhIHByZS1zcGVjaWZpZWQgYmF0Y2ggdmFyaWFibGUKcHJvamVjdGlvbl9wbG90cyA8LSBwbG90X1Byb2plY3Rpb25fYnlEb25vcigKICBxdWVyeV9vYmogPSBxdWVyeSwgCiAgYmF0Y2hfa2V5ID0gYmF0Y2hfa2V5LCAKICByZWZfb2JqID0gcmVmLCAKICBIaWVyYXJjaHlfb25seSA9IFRSVUUsICMgV2hldGhlciB0byBleGNsdWRlIFQvTksvUGxhc21hL1N0cm9tYWwgY2VsbHMgCiAgZG93bnNhbXBsZV9yZWZlcmVuY2UgPSBUUlVFLCAKICBkb3duc2FtcGxlX2ZyYWMgPSAwLjI1LCAgICMgZG93bi1zYW1wbGUgcmVmZXJlbmNlIGNlbGxzIHRvIDI1JTsgcmVkdWNlcyBmaWd1cmUgZmlsZSBzaXplCiAgcXVlcnlfcG9pbnRfc2l6ZSA9IDAuMiwgICAjIGFkanVzdCBzaXplIG9mIHF1ZXJ5IGNlbGxzIGJhc2VkIG9uICMgb2YgY2VsbHMKICBzYXZlcGxvdCA9IFRSVUUsIAogIHNhdmVfZm9sZGVyID0gJ3Byb2plY3Rpb25GaWd1cmVzLycKKQoKIyBzaG93IHBsb3RzIHRvZ2V0aGVyIHdpdGggcGF0Y2h3b3JrLiBDYW4gYWxzbyBqdXN0IGNhbGwgInByb2plY3Rpb25fcGxvdHMiIG9iamVjdCB0byBkaXNwbGF5IG9uZS1ieS1vbmUKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHByb2plY3Rpb25fcGxvdHMsIG5jb2wgPSAzKQpgYGAKCgojIyMgR2V0IENvbXBvc2l0aW9uIGRhdGEgZm9yIGVhY2ggZG9ub3IKCkhlcmUsIHRvIHN0dWR5IHRoZSBhYnVuZGFuY2Ugb2YgZWFjaCBjZWxsIHR5cGUgd2l0aGluIGVhY2ggZG9ub3IsIEkgZm9jdXMgb24gY2VsbHMgdGhhdCB3ZXJlIGNsYXNzaWZpZWQgd2l0aCBhIEtOTiBwcm9iID4gMC41ICh0aGF0IGlzLCA+NTAlIG9mIG5lYXJlc3QgbmVpZ2hib3VycyBmcm9tIHRoZSByZWZlcmVuY2UgbWFwIGFncmVlIG9uIHRoZSBhc3NpZ25lZCBjZWxsIHR5cGUpLiAKCldlIGNhbiBwcmVzZW50IHRoaXMgYXMgYSBsb25nIHRhYmxlCgpgYGB7cn0KcXVlcnlfY29tcG9zaXRpb24gPC0gZ2V0X0NvbXBvc2l0aW9uKAogIHF1ZXJ5X29iaiA9IHF1ZXJ5LCAKICBkb25vcl9rZXkgPSAnUGF0aWVudCcsIAogIGNlbGx0eXBlX2xhYmVsID0gJ3ByZWRpY3RlZF9DZWxsVHlwZScsIAogIG1hcFFDX2NvbCA9ICdtYXBwaW5nX2Vycm9yX1FDJywgCiAga25uX3Byb2JfY3V0b2ZmID0gMC41LCAKICByZXR1cm5fdHlwZSA9ICdsb25nJykKCnF1ZXJ5X2NvbXBvc2l0aW9uIApgYGAKCk9yIGFzIGEgd2lkZSB0YWJsZSB3aXRoIGNvdW50cyBvZiAjIG9mIGNlbGxzCgpgYGB7cn0KcXVlcnlfY29tcG9zaXRpb24gPC0gZ2V0X0NvbXBvc2l0aW9uKAogIHF1ZXJ5X29iaiA9IHF1ZXJ5LCAKICBkb25vcl9rZXkgPSAnUGF0aWVudCcsIAogIGNlbGx0eXBlX2xhYmVsID0gJ3ByZWRpY3RlZF9DZWxsVHlwZScsIAogIG1hcFFDX2NvbCA9ICdtYXBwaW5nX2Vycm9yX1FDJywgCiAga25uX3Byb2JfY3V0b2ZmID0gMC41LCAKICByZXR1cm5fdHlwZSA9ICdjb3VudCcpCgpxdWVyeV9jb21wb3NpdGlvbiAKYGBgCgpPciBhcyBhIHdpZGUgdGFibGUgd2l0aCBwcm9wb3J0aW9uIG9mIGVhY2ggY2VsbCB0eXBlIHdpdGhpbiBlYWNoIGRvbm9yCgpgYGB7cn0KcXVlcnlfY29tcG9zaXRpb24gPC0gZ2V0X0NvbXBvc2l0aW9uKAogIHF1ZXJ5X29iaiA9IHF1ZXJ5LCAKICBkb25vcl9rZXkgPSAnUGF0aWVudCcsIAogIGNlbGx0eXBlX2xhYmVsID0gJ3ByZWRpY3RlZF9DZWxsVHlwZScsIAogIG1hcFFDX2NvbCA9ICdtYXBwaW5nX2Vycm9yX1FDJywgCiAga25uX3Byb2JfY3V0b2ZmID0gMC41LCAKICByZXR1cm5fdHlwZSA9ICdwcm9wb3J0aW9uJykKCnF1ZXJ5X2NvbXBvc2l0aW9uIApgYGAKCgpgYGB7cn0KIyBTaW1wbGUgaGVhdG1hcCB0byB2aXN1YWxpemUgY29tcG9zaXRpb24gb2YgcHJvamVjdGVkIHNhbXBsZXMKcCA8LSBxdWVyeV9jb21wb3NpdGlvbiAlPiUgCiAgIyBzaG93IGNlbGx0eXBlcyBwcmVzZW50IGluID4xJSBvZiB0b3RhbCBjZWxscwogIHNlbGVjdChQYXRpZW50LCBjb2xuYW1lcyhxdWVyeV9jb21wb3NpdGlvbilbLTFdW2NvbFN1bXMocXVlcnlfY29tcG9zaXRpb25bLTFdKSA+IDAuMDFdKSAlPiUgCiAgIyBjb252ZXJ0IHRvIG1hdHJpeCBhbmQgZGlzcGxheSBoZWF0bWFwCiAgY29sdW1uX3RvX3Jvd25hbWVzKCdQYXRpZW50JykgJT4lIGRhdGEubWF0cml4KCkgJT4lIENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwKCkKcApgYGAKCgojIyMgRXh0cmE6IFNjb3JlIEFNTCBjZWxscyBmb3IgZW5yaWNobWVudCBvZiBMU0Mgc2lnbmF0dXJlcwoKSGVyZSB3ZSB3aWxsIGxvYWQgZ2VuZXNldHMgZGVyaXZlZCBmcm9tIGZ1bmN0aW9uYWwgc3R1ZGllcyBvZiBMU0MrIGFuZCBMU0MtIGZyYWN0aW9ucyBpbiBkaXZlcnNlIEFNTCBwYXRpZW50cyAoTmcgTmF0dXJlIDIwMTcpLCBhbmQgTFNDKyB2cyBMU0MtIGZyYWN0aW9ucyBpbiBwYXRpZW50cyB3aXRoIE1MTCB0cmFuc2xvY2F0aW9ucyAoU29tZXJ2YWlsbGUgQ2VsbCBTdGVtIENlbGwgMjAwOSkuIFRoZSBmb3JtZXIgdGVuZHMgdG8gY29ycmVzcG9uZCB0byBtb3JlIHByaW1pdGl2ZSBMU0NzIHdoaWxlIHRoZSBsYXR0ZXIgY29ycmVzcG9uZHMgdG8gYSBkaXN0aW5jdCBzaWduYXR1cmUgb2YgbWF0dXJlIHByb21vbm9jeXRpYyBMU0NzLgoKRnVydGhlciwgd2Ugd2lsbCBsb2FkIGFkZGl0aW9uYWwgZ2VuZXNldHMgZGVyaXZlZCBmcm9tIG1hcmtlciBnZW5lcyBvZiBsZXVrZW1pYSBjZWxsIHBvcHVsYXRpb25zIGRlZmluZWQgaW4gdmFuIEdhbGVuIGV0IGFsIENlbGwgMjAxOSBhbmQgWmVuZyBldCBhbCBOYXR1cmUgTWVkaWNpbmUgMjAyMi4gTm90YWJseSwgdGhlIExTUEMtUXVpZXNjZW50IHBvcHVsYXRpb24gaXMgaGlnaGx5IGFzc29jaWF0ZWQgd2l0aCBmdW5jdGlvbmFsIExTQ3MgYW5kIHJlbGFwc2UgYnkgZGVjb252b2x1dGlvbiAoWmVuZyAyMDIyKS4KCmBgYHtyfQpjdXJsOjpjdXJsX2Rvd25sb2FkKCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYW5keWd4emVuZy9Cb25lTWFycm93TWFwX0V4dHJhcy9tYWluL3NjQU1MX0RpZmZlcmVudGlhdGlvbl9TdGFnZV9NYXJrZXJHZW5lcy5nbXQnLCAKICAgICAgICAgICAgICAgICAgICBkZXN0ZmlsZSA9IHBhc3RlMChwcm9qZWN0aW9uX3BhdGgsICdzY0FNTF9EaWZmZXJlbnRpYXRpb25fU3RhZ2VfTWFya2VyR2VuZXMuZ210JykpCkFNTGdlbmVzZXRzIDwtIGxvYWRfR2VuZXNldHNfZ210KCdzY0FNTF9EaWZmZXJlbnRpYXRpb25fU3RhZ2VfTWFya2VyR2VuZXMuZ210JykKQU1MZ2VuZXNldHMgJT4lIHN1bW1hcnkoKQpgYGAKCmBgYHtyfQojIHNjb3JlIGdlbmVzZXRzIGJ5IEFVQ2VsbCBhbmQgYWRkIHRvIG1ldGFkYXRhCiMgdHlwaWNhbGx5IEkgc3BsaXQgdGhlIGRhdGEgaW50byBiYXRjaGVzIG9mIDVrLTEwayBjZWxscyB0byBjb25zZXJ2ZSBtZW1vcnkKcXVlcnkgPC0gc2NvcmVfR2VuZXNldHNfQVVDZWxsKHF1ZXJ5LCBnZW5lc2V0cyA9IEFNTGdlbmVzZXRzLCBuYmF0Y2hlcyA9IDEsIG5jb3JlcyA9IDEwLCBvdXRwdXQgPSAnbWV0YWRhdGEnKQpxdWVyeUBtZXRhLmRhdGEKYGBgCgpVc2luZyBnZW5lcyBhc3NvY2lhdGVkIHdpdGggZnVuY3Rpb25hbCBMU0MrIGVuZ3JhZnRtZW50IGNhcGFjaXR5IChMU0NfdnNfQmxhc3RfVVBfX05nMjAxNl9BVUMpIGFsb25nIHdpdGggdGhlIExTUEMtUXVpZXNjZW50IHNpZ25hdHVyZSBoZWxwIHVzIHRvIGlkZW50aWZ5IGNhbmRpZGF0ZSBMU0NzIHdpdGhpbiB0aGUgZGF0YS4gCgpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTJ9CkZlYXR1cmVQbG90KHN1YnNldChxdWVyeSwgbWFwcGluZ19lcnJvcl9RQyA9PSAnUGFzcycpLCAKICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCdMU1BDX1F1aWVzY2VudF9fWmVuZzIwMjJfQVVDJywgJ0xTQ192c19CbGFzdF9VUF9fTmcyMDE2X0FVQycpLCAKICAgICAgICAgICAgc3BsaXQuYnkgPSAnUGF0aWVudCcsIG1heC5jdXRvZmYgPSAncTk5JywgbWluLmN1dG9mZiA9ICdxNScpICYgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IHJldihicmV3ZXIucGFsKDExLCAnUmRCdScpKSkKYGBgCgpJdCBpcyBubyBzdXJwcmlzZSB0aGF0IHRoZXNlIGFyZSB0aGUgbW9zdCBwcmltaXRpdmUgY2VsbHMgZWFybGllc3QgaW4gUHNldWRvdGltZS4gTm90ZSB0aGF0IHRoaXMgYXBwcm9hY2ggZG9lcyBub3Qgd29yayBmb3IgdGhlIE1MTC1BRjkgc2FtcGxlcyB3aGVyZWluIGFsbCBsZXVrZW1pYyBjZWxscyBhcmUgbWF0dXJlIG15ZWxvaWQ7IGluIHRoaXMgY29udGV4dCB0aGUgTUxMX0xTQ19Tb21lcnZhaWxsZV8yMDA5X1VQIHNpZ25hdHVyZSBtYXkgaGVscCB1cyBpZGVudGlmeSBmdW5jdGlvbmFsICJMU0NzIiBjYXBhYmxlIG9mIGVuZ3JhZnRtZW50IHdpdGhpbiB0aGUgbWF0dXJlIG15ZWxvaWQgcG9wdWxhdGlvbnMgKHR5cGljYWxseSBsYXRlIEdNUHMgb3IgRWFybHkgUHJvTW9ub2N5dGVzKS4gCgpgYGB7ciwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9MTJ9CkZlYXR1cmVQbG90KHN1YnNldChxdWVyeSwgbWFwcGluZ19lcnJvcl9RQyA9PSAnUGFzcycpLCAKICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCdNTExfTFNDX1VQX19Tb21lcnZhaWxsZTIwMDlfQVVDJyksIAogICAgICAgICAgICBzcGxpdC5ieSA9ICdQYXRpZW50JywgbWF4LmN1dG9mZiA9ICdxOTknLCBtaW4uY3V0b2ZmID0gJ3E1JykgJiAKICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gcmV2KGJyZXdlci5wYWwoMTEsICdSZEJ1JykpKQpgYGAKCkFzIGV4cGVjdGVkLCBQcm9Nb25vY3l0ZXMgd2l0aGluIHRoZSBNTEwtQUY5IHNhbXBsZSBleGhpYml0IGVucmljaG1lbnQgZm9yIHRoZSBNTExfTFNDX1NvbWVydmFpbGxlXzIwMDlfVVAsIGluIGxpbmUgd2l0aCB0aGUgbWF0dXJlIG15ZWxvaWQgb3JpZ2luIG9mIHRoaXMgcGF0aWVudCdzIGRpc2Vhc2UuCgoKIyMjIFNhdmUgcHJvamVjdGlvbiByZXN1bHRzCgpUaGlzIHdpbGwgc2F2ZSBhIGNzdiBmaWxlIHdpdGggdGhlIG1hcHBlZCBhbm5vdGF0aW9ucyBmb3IgZWFjaCBjZWxsIChtYXBwaW5nIGVycm9yIHNjb3JlcywgdW1hcCBjb29yZGluYXRlcywgcHJlZGljdGVkIENlbGwgVHlwZSwgYW5kIHByZWRpY3RlZCBQc2V1ZG90aW1lKS4gV2UgYWxzbyBoYXZlIHRoZSBvcHRpb24gdG8gc2F2ZSBBVUNlbGwgc2NvcmVzLCBwcm92aWRlZCB0aGF0IHRoZXkgYXJlIGluIHRoZSBtZXRhZGF0YS4gCgpgYGB7cn0KIyBTYXZlIENlbGxUeXBlIEFubm90YXRpb25zIGFuZCBQcm9qZWN0ZWQgVU1BUCBjb29yZGluYXRlcwpzYXZlX1Byb2plY3Rpb25SZXN1bHRzKAogIHF1ZXJ5X29iaiA9IHF1ZXJ5LCAKICBjZWxsdHlwZV9sYWJlbCA9ICdwcmVkaWN0ZWRfQ2VsbFR5cGUnLCAKICBjZWxsdHlwZV9LTk5wcm9iX2xhYmVsID0gJ3ByZWRpY3RlZF9DZWxsVHlwZV9wcm9iJywgCiAgcHNldWRvdGltZV9sYWJlbCA9ICdwcmVkaWN0ZWRfUHNldWRvdGltZScsIAogIHNhdmVfQVVDZWxsX3Njb3JlcyA9IFRSVUUsCiAgZmlsZV9uYW1lID0gJ3F1ZXJ5ZGF0YV9wcm9qZWN0ZWRfbGFiZWxlZC5jc3YnKQpgYGAKCgojIyBOb3RlIG9uIERvd25zdHJlYW0gQW5hbHlzaXM6IAoKRm9yIGRvd25zdHJlYW0gYW5hbHlzaXMsIHlvdSBjYW4gdXNlIHRoZSBwcm9qZWN0ZWQgY2VsbHR5cGUgbGFiZWxzIHRvIGhlbHAgYW5ub3RhdGUgYW55IGxldWtlbWlhIGNlbGwgY2x1c3RlcnMgZ2VuZXJhdGVkIHRocm91Z2ggdW5zdXBlcnZpc2VkIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBhbmQgY2x1c3RlcmluZyBmcm9tIGluZGl2aWR1YWwgcGF0aWVudHMuIFNvbWV0aW1lcywgdW5zdXBlcnZpc2VkIGFuYWx5c2lzIHdpbGwgeWllbGQgY2x1c3RlcnMgY29ycmVzcG9uZGluZyB0byBkaWZmZXJlbnQgZGV2ZWxvcG1lbnRhbCBzdGF0ZXMgYW5kIG90aGVyIHRpbWVzIGl0IG1heSB5aWVsZCBjbHVzdGVycyBjb3JyZXNwb25kaW5nIHRvIGRpc3RpbmN0IHN1YmNsb25lcyB3aXRoaW4gdGhlIHBhdGllbnQuIEFsb25nIHdpdGggdG9vbHMgbGlrZSBpbmZlckNOViBmb3IgcGF0aWVudHMgd2l0aCBrbm93biBjeXRvZ2VuZXRpYyBhYm5vcm1hbGl0aWVzLCB0aGlzIGNhbiBiZSBpbnRlZ3JhdGVkIHRvIHZpc3VhbGl6ZSBjZWxsdWxhciBoaWVyYXJjaGllcyBhdCB0aGUgbGV2ZWwgb2YgaW5kaXZpZHVhbCBzdWJjbG9uZXMuIAoKQWRkaXRpb25hbCBpbmZvcm1hdGlvbiBmcm9tIHBzZXVkb3RpbWUgcHJvamVjdGlvbiBhbmQgc2NvcmluZyBvZiBMU0Mtc3BlY2lmaWMgc2lnbmF0dXJlcyBjYW4gaGVscCBpZGVudGlmeSBjYW5kaWRhdGUgTFNDcyB3aXRoaW4gdGhlIGRhdGEuIAoKRm9yIGRvd25zdHJlYW0gYW5hbHlzaXMsIGxldCdzIHRha2UgdGhlIGV4YW1wbGUgb2YgcHQzMDg4Niwgd2l0aCBpbnZvbHZlbWVudCBvZiBIU0NzIGFuZCBtdWx0aXBsZSB0cmFqZWN0b3JpZXMgc3Bhbm5pbmcgTXllbG9pZCwgRXJ5dGhyb2lkLCBhbmQgcERDIGRpZmZlcmVudGlhdGlvbjoKCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0xMn0KcHQzMDg4NiA8LSBxdWVyeSAlPiUKICBzdWJzZXQoLiwgUGF0aWVudCA9PSAncHRfMzA4ODYnKSAlPiUgCiAgU0NUcmFuc2Zvcm0oKSAlPiUgCiAgUnVuUENBKCkgJT4lIAogIFJ1blVNQVAocmVkdWN0aW9uID0gJ3BjYScsIGRpbXM9MToyMCkgJT4lCiAgRmluZE5laWdoYm9ycyhyZWR1Y3Rpb24gPSAncGNhJywgZGltcyA9IDE6MjApICU+JSAKICBGaW5kQ2x1c3RlcnMocmVzb2x1dGlvbiA9IDEsIGFsZ29yaXRobSA9IDMpCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTEyfQpEaW1QbG90KHB0MzA4ODYsIHJlZHVjdGlvbiA9ICd1bWFwJywgZ3JvdXAuYnk9Yygnc2V1cmF0X2NsdXN0ZXJzJywgJ3ByZWRpY3RlZF9DZWxsVHlwZV9Ccm9hZCcpLCBsYWJlbCA9IFQpCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTEzfQpGZWF0dXJlUGxvdChwdDMwODg2LCAKICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCdMU1BDX1F1aWVzY2VudF9fWmVuZzIwMjJfQVVDJywgJ0xTQ192c19CbGFzdF9VUF9fTmcyMDE2X0FVQycsICdIU0NfTVBQX2xpa2VfX1RvcDEwMF9zY0FNTF9BVUMnKSwgCiAgICAgICAgICAgIG1heC5jdXRvZmYgPSAncTk5JywgbWluLmN1dG9mZiA9ICdxNScsIG5jb2wgPSAzKSAmIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSByZXYoYnJld2VyLnBhbCgxMSwgJ1JkQnUnKSkpCmBgYAoKQmFzZWQgb24gdGhpcyB3ZSBjYW4gc2VlIG11bHRpcGxlIHRyYW5zY3JpcHRpb25hbCBjbHVzdGVycyB3aXRoaW4gdGhlIGRhdGEsIGluY2x1ZGluZyBzb21lIG5vbi1sZXVrZW1pYyBjbHVzdGVycyBvZiBUIGNlbGxzLCBCIGNlbGxzLCBhbmQgUGxhc21hIGNlbGxzLiAKQ2x1c3RlciAzIGNvbnRhaW5zIEhTQy9NUFAtbGlrZSBjZWxscyBtb3N0IGVucmljaGVkIGZvciBMU0Mgc2lnbmF0dXJlcy4gCgpMZXQncyBzZWUgd2hhdCBlYWNoIGNsdXN0ZXIgbG9va3MgbGlrZSB3aGVuIHByb2plY3RlZCBvbnRvIEJvbmVNYXJyb3dNYXA6CgpgYGB7ciwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9OX0KRGltUGxvdChwdDMwODg2LCByZWR1Y3Rpb24gPSAndW1hcF9wcm9qZWN0ZWQnLCBncm91cC5ieT1jKCdzZXVyYXRfY2x1c3RlcnMnLCAncHJlZGljdGVkX0NlbGxUeXBlX0Jyb2FkJyksIGxhYmVsID0gVCkKYGBgCgpgYGB7cn0KcHQzMDg4NkBtZXRhLmRhdGEgJT4lIAogICMgY29tcGFyZSBjbHVzdGVycyBhZ2FpbnN0IGJyb2FkIGNlbGwgdHlwZXMgYXNzaWdubWVudHMgCiAgZ3JvdXBfYnkocHJlZGljdGVkX0NlbGxUeXBlX0Jyb2FkLCBzZXVyYXRfY2x1c3RlcnMpICU+JSAKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JSB1bmdyb3VwKCkgJT4lIAogICMgZ2V0IHByb3BvcnRpb25zIG9mIGNlbGxzIHdpdGhpbiBlYWNoIGNsdXN0ZXIgYXNzaWduZWQgYXMgYSBjZXJ0YWluIGNlbGx0eXBlIAogIGdyb3VwX2J5KHNldXJhdF9jbHVzdGVycykgJT4lIG11dGF0ZShwcm9wID0gY291bnQgLyBzdW0oY291bnQpKSAlPiUgCiAgcGl2b3Rfd2lkZXIoaWRfY29scyA9ICdwcmVkaWN0ZWRfQ2VsbFR5cGVfQnJvYWQnLCBuYW1lc19mcm9tID0gJ3NldXJhdF9jbHVzdGVycycsIHZhbHVlc19mcm9tID0gJ3Byb3AnKSAlPiUgCiAgIyBmaWxsIGluIHplcm9zIGFuZCBmb3JtYXQgZm9yIGhlYXRtYXAgCiAgcmVwbGFjZShpcy5uYSguKSwgMCkgJT4lIGNvbHVtbl90b19yb3duYW1lcygncHJlZGljdGVkX0NlbGxUeXBlX0Jyb2FkJykgJT4lIAogICMgY3JlYXRlIGEgYmFzaWMgaGVhdG1hcCB0byB2aXN1YWxpemUgY2VsbCB0eXBlcyB2cyBjbHVzdGVycwogIHBoZWF0bWFwOjpwaGVhdG1hcCgpCmBgYAoKVGhpcyBpbGx1c3RyYXRlcyBob3cgYW5ub3RhdGlvbnMgZnJvbSBCb25lTWFycm93TWFwIGNhbiBhbGxvdyB1cyB0byBhbmFseXplIGNsdXN0ZXJzIG9mIGxldWtlbWljIGNlbGxzIGlkZW50aWZpZWQgZnJvbSB1bnN1cGVydmlzZWQgYW5hbHlzaXMuIApBcyBzaG93biBpbiB0aGUgaGVhdG1hcCBhYm92ZSwgZWFjaCBjbHVzdGVyIGZyb20gdGhpcyBwYXRpZW50IGFjdHVhbGx5IGNvcnJlc3BvbmRzIHRvIGEgZGlzdGluY3Qgc3RhdGUgYWxvbmcgdGhlIGhlbWF0b3BvaWV0aWMgaGllcmFyY2h5LCB3aXRoIG11bHRpcGxlIGNsdXN0ZXJzIGNvcnJlc3BvbmRpbmcgdG8gZXJ5dGhyb2lkIHByb2dlbml0b3JzLiBPdmVybGF5aW5nIGNvcHkgbnVtYmVyIGFsdGVyYXRpb24gY2FsbHMgZnJvbSBpbmZlckNOViBvciBleHByZXNzZWQgbXV0YXRpb25zIGNhbiBoZWxwIHVzIGlkZW50aWZ5IHRoZSBkaWZmZXJlbnRpYXRpb24gcGF0dGVybnMgb2YgZGlzdGluY3Qgc3ViY2xvbmVzIHdpdGhpbiB0aGlzIHBhdGllbnQuIAoKCkZpbmFsbHksIGZvciBjb2hvcnRzIHdpdGggbWFueSBwYXRpZW50cyBjb21wb3NpdGlvbiBhbmFseXNpcyBjYW4gYmUgcGVyZm9ybWVkIHRvIHVuZGVyc3RhbmQgaG93IGxldWtlbWlhIGNlbGwgaGllcmFyY2hpZXMgdmFyeSB3aXRoIHBhdGllbnQgY2hhcmFjdGVyaXN0aWNzIGFuZCB0aGVyYXB5IHJlc3BvbnNlIC8gcmVsYXBzZS4gSSBob3BlIHRoaXMgdHV0b3JpYWwgd2FzIHVzZWZ1bCBhbmQgZmVlbCBmcmVlIHRvIGNvbW1lbnQgd2l0aCBhbnkgcXVlc3Rpb25zIHlvdSBtYXkgaGF2ZSBhcm91bmQgcHJvamVjdGlvbiBvZiBsZXVrZW1pYSBjZWxscyBvciBkb3duc3RyZWFtIGFuYWx5c2lzLgo=